﻿#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <windows.h>

#pragma comment(lib, "ws2_32.lib")  // 链接Winsock库

HINSTANCE g_hInstance = 0;
HWND hChatWnd = 0;        // 聊天内容显示框（只读）
HWND hInputWnd = 0;       // 消息输入框
HWND hSendBtn = 0;        // 发送按钮
SOCKET server_socket = INVALID_SOCKET;  // 服务器监听套接字
HANDLE server_thread = NULL;            // 服务器主线程句柄
BOOL is_server_running = FALSE;         // 服务器运行状态标记

// 客户端相关定义
#define MAX_CLIENTS 10      // 最大客户端连接数
#define BUFFER_SIZE 1024    // 消息缓冲区大小
#define NAME_SIZE 50        // 用户名最大长度

typedef struct {
    SOCKET socket;          // 客户端套接字
    char name[NAME_SIZE];   // 客户端用户名
    int active;             // 客户端活跃状态（1=在线，0=离线）
} Client;

Client clients[MAX_CLIENTS];        // 客户端数组
int client_count = 0;               // 在线客户端数量
CRITICAL_SECTION clients_mutex;     // 客户端数组同步锁
CRITICAL_SECTION ui_mutex;          // 界面更新同步锁


// -------------------------- 工具函数声明 --------------------------
// 跨线程更新界面文本（解决UI线程安全问题）
void AddTextToChatWnd(const wchar_t* text);
// 服务器主线程函数（监听客户端连接）
DWORD WINAPI ServerMainThread(LPVOID arg);
// 客户端处理线程函数（接收客户端消息并广播）
DWORD WINAPI HandleClient(LPVOID arg);
// 广播消息给所有在线客户端
void BroadcastMessage(const char* message, size_t sender_index);
// 清理服务器资源
void CleanupServer();


// -------------------------- Win32界面回调函数 --------------------------
LRESULT CALLBACK WndProc(
    HWND    hWnd,
    UINT    uMsg,
    WPARAM  wParam,
    LPARAM  lParam
) {
    static BOOL is_first_send = TRUE;  // 标记是否首次发送（首次发送端口号）

    switch (uMsg) {
        // 1. 创建界面控件
    case WM_CREATE: {
        // 初始化临界区（线程同步用）
        InitializeCriticalSection(&clients_mutex);
        InitializeCriticalSection(&ui_mutex);

        // 创建聊天显示框（只读、多行、带滚动条）
        hChatWnd = CreateWindowW(
            L"EDIT", L"",
            WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE |
            ES_AUTOVSCROLL | ES_READONLY | WS_BORDER,
            10, 10, 500, 300,
            hWnd, (HMENU)1001, g_hInstance, NULL
        );
        SetWindowTextW(hChatWnd, L"请输入端口号（5000-65535）：");
        // 光标定位到初始文本末尾
        int text_len = GetWindowTextLengthW(hChatWnd);
        SendMessageW(hChatWnd, EM_SETSEL, text_len, text_len);

        // 创建消息输入框
        hInputWnd = CreateWindowW(
            L"EDIT", L"",
            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
            10, 320, 400, 30,
            hWnd, (HMENU)1002, g_hInstance, NULL
        );

        // 创建发送按钮
        hSendBtn = CreateWindowW(
            L"BUTTON", L"启动服务器/发送",
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            420, 320, 120, 30,
            hWnd, (HMENU)1003, g_hInstance, NULL
        );
        break;
    }

    case WM_COMMAND: {
        int ctrl_id = LOWORD(wParam);
        int event_type = HIWORD(wParam);

        // 发送按钮点击
        if (ctrl_id == 1003 && event_type == BN_CLICKED) {
            int input_len = GetWindowTextLengthW(hInputWnd) + 1;
            if (input_len <= 1) {  // 空输入校验
                MessageBoxW(hWnd, L"请输入内容", L"提示", MB_OK | MB_ICONINFORMATION);
                break;
            }

            wchar_t input_text[1024] = { 0 };
            GetWindowTextW(hInputWnd, input_text, input_len);
            wchar_t chat_msg[1024] = { 0 };

            // 首次发送：校验端口号并启动服务器
            if (is_first_send) {
                is_first_send = FALSE;
                BOOL is_valid = TRUE;

                // 步骤1：校验输入是否为整数
                for (int i = 0; input_text[i] != L'\0'; i++) {
                    if (input_text[i] < L'0' || input_text[i] > L'9') {
                        is_valid = FALSE;
                        break;
                    }
                }
                if (input_text[0] == L'\0') is_valid = FALSE;

                // 步骤2：校验端口号范围（5000-65535）
                int port = 0;
                if (is_valid) {
                    port = _wtoi(input_text);
                    if (port < 5000 || port > 65535) is_valid = FALSE;
                }

                // 无效端口号：提示并恢复首次发送状态
                if (!is_valid) {
                    MessageBoxW(hWnd, L"请输入5000-65535之间的整数", L"输入错误", MB_OK | MB_ICONERROR);
                    is_first_send = TRUE;
                    break;
                }

                // 有效端口号：启动服务器线程
                is_server_running = TRUE;
                int* port_ptr = (int*)malloc(sizeof(int));
                *port_ptr = port;
                server_thread = CreateThread(
                    NULL, 0,
                    ServerMainThread,  // 服务器主线程入口
                    port_ptr,          // 传递端口号参数
                    0, NULL
                );
                if (server_thread == NULL) {
                    MessageBoxW(hWnd, L"服务器启动失败", L"错误", MB_OK | MB_ICONERROR);
                    is_first_send = TRUE;
                    free(port_ptr);
                    break;
                }
                CloseHandle(server_thread);  // 释放线程句柄（不影响线程运行）

                // 显示服务器启动信息
                swprintf_s(chat_msg, sizeof(chat_msg) / sizeof(wchar_t),
                    L"%s\r\n服务器已启动，等待客户端连接...\r\n", input_text);
                AddTextToChatWnd(chat_msg);
            }
            // 非首次发送：向所有客户端广播消息
            else {
                if (!is_server_running) {
                    MessageBoxW(hWnd, L"服务器未启动", L"提示", MB_OK | MB_ICONINFORMATION);
                    break;
                }

                // 转换宽字符串为多字节字符串（Socket传输用）
                char send_msg[BUFFER_SIZE + NAME_SIZE + 4] = { 0 };
                char input_multi[1024] = { 0 };
                WideCharToMultiByte(CP_ACP, 0, input_text, -1,
                    input_multi, sizeof(input_multi), NULL, NULL);

                // 格式化服务器消息
                sprintf_s(send_msg, sizeof(send_msg), "服务器: %s\n", input_multi);
                // 广播消息给所有客户端
                BroadcastMessage(send_msg, MAX_CLIENTS);  // 用MAX_CLIENTS表示发送者是服务器
                // 本地显示消息
                swprintf_s(chat_msg, sizeof(chat_msg) / sizeof(wchar_t),
                    L"我（服务器）: %s\r\n", input_text);
                AddTextToChatWnd(chat_msg);
            }

            // 清空输入框
            SetWindowTextW(hInputWnd, L"");
        }
        break;
    }

    case WM_SIZE: {
        int client_width = LOWORD(lParam);
        int client_height = HIWORD(lParam);

        if (hChatWnd) MoveWindow(hChatWnd, 10, 10, client_width - 20, client_height - 70, TRUE);
        if (hInputWnd) MoveWindow(hInputWnd, 10, client_height - 50, client_width - 140, 30, TRUE);
        if (hSendBtn) MoveWindow(hSendBtn, client_width - 120, client_height - 50, 110, 30, TRUE);
        break;
    }

    case WM_DESTROY:
        CleanupServer();  // 关闭服务器和客户端连接
        DeleteCriticalSection(&clients_mutex);
        DeleteCriticalSection(&ui_mutex);
        PostQuitMessage(0);
        break;
    }
    return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}

// -------------------------- 服务器核心函数实现 --------------------------
// 1. 跨线程更新聊天框文本（UI线程安全）
void AddTextToChatWnd(const wchar_t* text) {
    EnterCriticalSection(&ui_mutex);  // 锁定UI更新

    // 定位到聊天框末尾并追加文本
    SendMessageW(hChatWnd, EM_SETSEL, (WPARAM)-1, (LPARAM)-1);
    SendMessageW(hChatWnd, EM_REPLACESEL, FALSE, (LPARAM)text);
    // 滚动到最底部
    SendMessageW(hChatWnd, WM_VSCROLL, SB_BOTTOM, 0);

    LeaveCriticalSection(&ui_mutex);  // 解锁
}

// 2. 广播消息给所有在线客户端
void BroadcastMessage(const char* message, size_t sender_index) {
    EnterCriticalSection(&clients_mutex);  // 锁定客户端数组

    for (int i = 0; i < MAX_CLIENTS; i++) {
        // 只发送给活跃且非发送者的客户端
        if (clients[i].active && i != sender_index) {
            send(clients[i].socket, message, strlen(message), 0);
        }
    }

    LeaveCriticalSection(&clients_mutex);  // 解锁
}

// 3. 处理单个客户端的线程（接收消息+广播）
DWORD WINAPI HandleClient(LPVOID arg) {
    int client_index = *(int*)arg;
    free(arg);  // 释放参数内存

    char buffer[BUFFER_SIZE] = { 0 };
    int bytes_read = 0;
    char message[BUFFER_SIZE + NAME_SIZE + 4] = { 0 };
    wchar_t w_message[BUFFER_SIZE + NAME_SIZE + 4] = { 0 };

    // 步骤1：接收客户端用户名
    if ((bytes_read = recv(clients[client_index].socket, clients[client_index].name, NAME_SIZE - 1, 0)) <= 0) {
        AddTextToChatWnd(L"接收客户端用户名失败\r\n");
        goto cleanup;
    }
    clients[client_index].name[bytes_read] = '\0';
    // 移除换行符
    if (clients[client_index].name[strlen(clients[client_index].name) - 1] == '\n') {
        clients[client_index].name[strlen(clients[client_index].name) - 1] = '\0';
    }

    // 步骤2：通知所有客户端（含本地界面）有新用户加入
    sprintf_s(message, sizeof(message), "%s 加入了聊天室\n", clients[client_index].name);
    BroadcastMessage(message, client_index);
    // 本地界面显示
    MultiByteToWideChar(CP_ACP, 0, message, -1, w_message, sizeof(w_message) / sizeof(wchar_t));
    AddTextToChatWnd(w_message);

    // 步骤3：循环接收客户端消息并广播
    while ((bytes_read = recv(clients[client_index].socket, buffer, BUFFER_SIZE - 1, 0)) > 0) {
        buffer[bytes_read] = '\0';
        // 移除换行符
        if (buffer[strlen(buffer) - 1] == '\n') {
            buffer[strlen(buffer) - 1] = '\0';
        }

        // 客户端发送"exit"：断开连接
        if (strncmp(buffer, "exit", 4) == 0) break;

        // 格式化消息并广播
        sprintf_s(message, sizeof(message), "%s: %s\n", clients[client_index].name, buffer);
        BroadcastMessage(message, client_index);
        // 本地界面显示
        MultiByteToWideChar(CP_ACP, 0, message, -1, w_message, sizeof(w_message) / sizeof(wchar_t));
        AddTextToChatWnd(w_message);
    }

    // 步骤4：客户端断开连接，清理资源
    sprintf_s(message, sizeof(message), "%s 离开了聊天室\n", clients[client_index].name);
    BroadcastMessage(message, client_index);
    MultiByteToWideChar(CP_ACP, 0, message, -1, w_message, sizeof(w_message) / sizeof(wchar_t));
    AddTextToChatWnd(w_message);

cleanup:
    EnterCriticalSection(&clients_mutex);
    closesocket(clients[client_index].socket);
    clients[client_index].active = 0;
    client_count--;
    LeaveCriticalSection(&clients_mutex);

    return 0;
}

// 4. 服务器主线程（监听客户端连接）
DWORD WINAPI ServerMainThread(LPVOID arg) {
    int port = *(int*)arg;
    free(arg);  // 释放端口号参数内存

    WSADATA wsa_data;
    struct sockaddr_in server_addr, client_addr;
    int addr_len = sizeof(struct sockaddr_in);
    SOCKET new_socket = INVALID_SOCKET;
    HANDLE client_thread = NULL;
    int* client_index_ptr = NULL;

    // 初始化Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
        wchar_t err_msg[128] = { 0 };
        swprintf_s(err_msg, sizeof(err_msg) / sizeof(wchar_t),
            L"Winsock初始化失败，错误码: %d\r\n", WSAGetLastError());
        AddTextToChatWnd(err_msg);
        is_server_running = FALSE;
        return 1;
    }

    // 初始化客户端数组
    EnterCriticalSection(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        clients[i].socket = INVALID_SOCKET;
        clients[i].name[0] = '\0';
        clients[i].active = 0;
    }
    client_count = 0;
    LeaveCriticalSection(&clients_mutex);

    // 创建服务器监听套接字
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == INVALID_SOCKET) {
        wchar_t err_msg[128] = { 0 };
        swprintf_s(err_msg, sizeof(err_msg) / sizeof(wchar_t),
            L"创建套接字失败，错误码: %d\r\n", WSAGetLastError());
        AddTextToChatWnd(err_msg);
        WSACleanup();
        is_server_running = FALSE;
        return 1;
    }

    // 设置服务器地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;  // 监听所有网卡
    server_addr.sin_port = htons(port);        // 转换端口号为网络字节序

    // 绑定套接字到端口
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
        wchar_t err_msg[128] = { 0 };
        swprintf_s(err_msg, sizeof(err_msg) / sizeof(wchar_t),
            L"绑定端口失败，错误码: %d\r\n", WSAGetLastError());
        AddTextToChatWnd(err_msg);
        closesocket(server_socket);
        WSACleanup();
        is_server_running = FALSE;
        return 1;
    }

    // 开始监听客户端连接（最大等待队列3）
    if (listen(server_socket, 3) == SOCKET_ERROR) {
        wchar_t err_msg[128] = { 0 };
        swprintf_s(err_msg, sizeof(err_msg) / sizeof(wchar_t),
            L"监听失败，错误码: %d\r\n", WSAGetLastError());
        AddTextToChatWnd(err_msg);
        closesocket(server_socket);
        WSACleanup();
        is_server_running = FALSE;
        return 1;
    }

    // 循环接受客户端连接
    while (is_server_running) {
        new_socket = accept(server_socket, (struct sockaddr*)&client_addr, &addr_len);
        if (new_socket == INVALID_SOCKET) {
            if (is_server_running) {  // 排除服务器主动关闭的情况
                wchar_t err_msg[128] = { 0 };
                swprintf_s(err_msg, sizeof(err_msg) / sizeof(wchar_t),
                    L"接受连接失败，错误码: %d\r\n", WSAGetLastError());
                AddTextToChatWnd(err_msg);
            }
            continue;
        }

        EnterCriticalSection(&clients_mutex);
        // 查找空闲的客户端槽位
        int client_index = -1;
        for (int i = 0; i < MAX_CLIENTS; i++) {
            if (!clients[i].active) {
                client_index = i;
                break;
            }
        }

        // 客户端已满：拒绝连接
        if (client_index == -1) {
            AddTextToChatWnd(L"客户端连接数已满，拒绝新连接\r\n");
            closesocket(new_socket);
            LeaveCriticalSection(&clients_mutex);
            continue;
        }

        // 保存新客户端信息
        clients[client_index].socket = new_socket;
        clients[client_index].active = 1;
        client_count++;
        LeaveCriticalSection(&clients_mutex);

        // 创建客户端处理线程
        client_index_ptr = (int*)malloc(sizeof(int));
        *client_index_ptr = client_index;
        client_thread = CreateThread(
            NULL, 0,
            HandleClient,    // 客户端处理线程入口
            client_index_ptr,// 传递客户端索引
            0, NULL
        );
        if (client_thread == NULL) {
            wchar_t err_msg[128] = { 0 };
            swprintf_s(err_msg, sizeof(err_msg) / sizeof(wchar_t),
                L"创建客户端线程失败，错误码: %d\r\n", GetLastError());
            AddTextToChatWnd(err_msg);
            free(client_index_ptr);
            EnterCriticalSection(&clients_mutex);
            closesocket(clients[client_index].socket);
            clients[client_index].active = 0;
            client_count--;
            LeaveCriticalSection(&clients_mutex);
        }
        else {
            CloseHandle(client_thread);  // 释放线程句柄
        }
    }

    // 服务器线程退出：清理Winsock
    WSACleanup();
    return 0;
}

// 5. 清理服务器资源（关闭套接字、停止线程）
void CleanupServer() {
    is_server_running = FALSE;  // 通知服务器线程退出

    // 关闭服务器监听套接字
    if (server_socket != INVALID_SOCKET) {
        closesocket(server_socket);
        server_socket = INVALID_SOCKET;
    }

    // 关闭所有客户端套接字
    EnterCriticalSection(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (clients[i].active && clients[i].socket != INVALID_SOCKET) {
            closesocket(clients[i].socket);
            clients[i].active = 0;
        }
    }
    client_count = 0;
    LeaveCriticalSection(&clients_mutex);
}


int WINAPI WinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPSTR lpCmdLine,
    _In_ int nShowCmd
) {
    g_hInstance = hInstance;

    WNDCLASSW wnd_class = { 0 };
    wnd_class.hInstance = hInstance;
    wnd_class.lpszClassName = L"ChatServerWindow";
    wnd_class.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
    wnd_class.lpfnWndProc = WndProc;
    wnd_class.hCursor = LoadCursor(NULL, IDC_ARROW);


    if (!RegisterClassW(&wnd_class)) {
        MessageBoxW(NULL, L"窗口类注册失败", L"错误", MB_OK | MB_ICONERROR);
        return 1;
    }

    HWND hwnd = CreateWindowW(
        wnd_class.lpszClassName,
        L"聊天服务器",
        WS_OVERLAPPEDWINDOW,  // 带标题栏、最大化/最小化按钮
        450, 200, 800, 600,   // 窗口位置（x,y）和大小（宽,高）
        NULL, NULL, hInstance, NULL
    );
    if (!hwnd) {
        MessageBoxW(NULL, L"窗口创建失败", L"错误", MB_OK | MB_ICONERROR);
        return 1;
    }

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);

    MSG msg = { 0 };
    while (1)
    {
        if (PeekMessageW(&msg, NULL, 0, 0, 0))
        {
            if (GetMessageW(&msg, NULL, 0, 0))
            {
                TranslateMessage(&msg);
                DispatchMessageW(&msg);
            }
            else {
                return 0;
            }
        }
        else {
            //

        }
    }
    return 0;
}